﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Input;
using ImageTagToolViewModel.Interfaces;
using ImageTagToolViewModel.Models;
using ImageTagToolViewModel.Others;
using ImageTagToolViewModel.Serialization;

namespace ImageTagToolViewModel.ViewModels
{
    /// <summary>
    /// View-Model of the MainWindow.
    /// </summary>
    public class MainWindowViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private ImageProcessingModel _ImageProcessingModel;
        private TagProcessingModel _TagProcessingModel;

        private IMainWindow _MainWindow;
        internal IMainWindow MainWindow
        {
            get { return _MainWindow; }
        }

        /// <summary>
        /// Gets the ImageProcessingModel for internal communication between components.
        /// </summary>
        internal ImageProcessingModel ImageProcessingModel
        {
            get { return _ImageProcessingModel; }
        }

        #region UIBindings
        private bool _GeneralDataGroupBoxEnabled = false;
        /// <summary>Controls wether the groupbox containing the pictures general data is enabled.</summary>
        public bool GeneralDataGroupBoxEnabled
        {
            get { return _GeneralDataGroupBoxEnabled; }
            set
            {
                _GeneralDataGroupBoxEnabled = value;
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("GeneralDataGroupBoxEnabled"));
            }
        }

        private bool _TagsGroupBoxEnabled = false;
        /// <summary>Controls wether the groupbox containing the tags' data is enabled.</summary>
        public bool TagsGroupBoxEnabled
        {
            get { return _TagsGroupBoxEnabled; }
            set
            {
                _TagsGroupBoxEnabled = value;
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("TagsGroupBoxEnabled"));
            }
        }

        private bool _ContextRelatedMenuItemsEnabled = false;
        /// <summary>Controls wether the groupbox containing the tags' data is enabled.</summary>
        public bool ContextRelatedMenuItemsEnabled
        {
            get { return _ContextRelatedMenuItemsEnabled; }
            set
            {
                _ContextRelatedMenuItemsEnabled = value;
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("ContextRelatedMenuItemsEnabled"));
            }
        }

        private string _StatusBarCounter;
        /// <summary>Shows which is the actual picture out of the all of them.</summary>
        public string StatusBarCounter
        {
            get { return _StatusBarCounter; }
            private set
            {
                _StatusBarCounter = value;
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("StatusBarCounter"));
            }
        }

        private string _StatusBarResolution;
        /// <summary>Shows the actual picture's dimensions.</summary>
        public string StatusBarResolution
        {
            get { return _StatusBarResolution; }
            private set
            {
                _StatusBarResolution = value;
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("StatusBarResolution"));
            }
        }

        private string _StatusBarSize;
        /// <summary>Shows the actual picture's size.</summary>
        public string StatusBarSize
        {
            get { return _StatusBarSize; }
            private set
            {
                _StatusBarSize = value;
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("StatusBarSize"));
            }
        }

        private bool _LoadProgressBarVisibility;
        /// <summary>Is the Status-Bar's Progress-Bar visible.</summary>
        public bool LoadProgressBarVisibility
        {
            get { return _LoadProgressBarVisibility; }
            internal set
            {
                _LoadProgressBarVisibility = value;
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("LoadProgressBarVisibility"));
            }
        }

        private int _LoadProgressBarValue;
        /// <summary>The actual value of the Status-Bar's Progress-Bar.</summary>
        public int LoadProgressBarValue
        {
            get { return _LoadProgressBarValue; }
            internal set
            {
                _LoadProgressBarValue = value;
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("LoadProgressBarValue"));
            }
        }

        private bool _NextButtonIsEnabled;
        /// <summary>Can the user press the NextButton.</summary>
        public bool NextButtonIsEnabled
        {
            get { return _NextButtonIsEnabled; }
            internal set
            {
                _NextButtonIsEnabled = value;
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("NextButtonIsEnabled"));
            }
        }

        private bool _PreviousButtonIsEnabled;
        /// <summary>Can the user press the PrevoiusButton.</summary>
        public bool PreviousButtonIsEnabled
        {
            get { return _PreviousButtonIsEnabled; }
            internal set
            {
                _PreviousButtonIsEnabled = value;
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("PreviousButtonIsEnabled"));
            }
        }

        private string _NameTextBoxText;
        /// <summary>The name of the image file.</summary>
        public string NameTextBoxText
        {
            get { return _NameTextBoxText; }
            internal set
            {
                _NameTextBoxText = value;
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("NameTextBoxText"));
            }
        }

        /// <summary>The value of the slider indicating the actual picture's rating. Two way bindig.</summary>
        public double RatingSliderValue
        {
            get
            {
                if (PictureHandler.Instance.ActualPicture == null)
                    return 3.0;
                return PictureHandler.Instance.ActualPicture.Rating / 2.0;
            }
            set
            {
                PictureHandler.Instance.ActualPicture.Rating = (byte)(2 * value);
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("RatingSliderValue"));
            }
        }

        private string _TagTextBoxText;
        /// <summary>The text of the tag input box. Two way bindig.</summary>
        public string TagTextBoxText
        {
            get { return _TagTextBoxText; }
            set
            {
                _TagTextBoxText = value;
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("TagTextBoxText"));
                _TagProcessingModel.TagTextBoxTextChanged(value);
            }
        }

        private ObservableCollection<string> _DBTagsList;
        /// <summary>List of all tags, belonging to the selected tag-group.</summary>
        public ObservableCollection<string> DBTagsList
        {
            get { return _DBTagsList; }
            private set
            {
                _DBTagsList = value;
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("DBTagsList"));
            }
        }

        private ObservableCollection<string> _ImageTagsList;
        /// <summary>List of all tags, belonging to the picture's selected tag-group.</summary>
        public ObservableCollection<string> ImageTagsList
        {
            get { return _ImageTagsList; }
            private set
            {
                _ImageTagsList = value;
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs("ImageTagsList"));
                //PictureHandler.Instance.ActualPicture
            }
        }
        #endregion UIBindings

        #region Actions
        public DelegateCommand NewMenuItemClicked { get; private set; }
        public DelegateCommand CloseMenuItemClicked { get; private set; }
        public DelegateCommand AboutMenuItemClicked { get; private set; }
        public DelegateCommand NextButtonClicked { get; private set; }
        public DelegateCommand PreviousButtonClicked { get; private set; }
        public DelegateCommand SceneRadioButtonClicked { get; private set; }
        public DelegateCommand PersonRadioButtonClicked { get; private set; }
        public DelegateCommand OtherRadioButtonClicked { get; private set; }
        public DelegateCommand ToFullSizeMenuItemClicked { get; private set; }
        public DelegateCommand ApproximateMemoryUsageClicked { get; private set; }
        public DelegateCommand AddOtherLocationMenuItemClicked { get; private set; }
        public DelegateCommand CloseButtonClicked { get; private set; }
        public DelegateCommand WindowLoaded { get; private set; }
        #endregion Actions

        /// <summary>
        /// Initializes a new MainWindowViewModel object.
        /// </summary>
        /// <param name="mainWindow">Reference of the using MainWindow</param>
        public MainWindowViewModel(IMainWindow mainWindow)
        {
            _MainWindow = mainWindow;
            _ImageProcessingModel = new ImageProcessingModel(this);
            _TagProcessingModel = new TagProcessingModel(this);

            _DBTagsList = new ObservableCollection<string>();
            _ImageTagsList = new ObservableCollection<string>();

            SetUpCommands();
        }

        /// <summary>
        /// Initializes the event handlers of the UI.
        /// </summary>
        private void SetUpCommands()
        {
            // File menu.
            NewMenuItemClicked = new DelegateCommand(o => OnNewMenuItemClicked());

            AddOtherLocationMenuItemClicked = new DelegateCommand(o =>
                {
                    string path = _MainWindow.OpenFolder();
                    if (string.IsNullOrEmpty(path))
                        return;
                    _ImageProcessingModel.AddOtherLocation(path);
                });

            CloseMenuItemClicked = new DelegateCommand(o =>
                {
                    // Here comes the code (method call), what to do before closing the window.
                    // Saving the workflow.
                    SerializationProvider.SaveData();

                    // Closing the main window.
                    _MainWindow.CloseWindow();
                });

            // About menu.
            AboutMenuItemClicked = new DelegateCommand(o =>
                {
                    // Showing the About-box.
                    _MainWindow.ShowAboutBox();
                });

            ApproximateMemoryUsageClicked = new DelegateCommand(o =>
                {
                    int memory = 0;
                    memory += PictureHandler.Instance.ApproximateMemoryUsage();
                    memory += TagDatabase.Instance.ApproximateMemoryUsage();

                    string message = "Memory usage is about: ";
                    if (memory < 1024)
                        message += memory + " B.";
                    else if (memory < 1048576)
                    {
                        message += (memory / 1024.0f).ToString("F") + " KB.";
                    }
                    else
                        message += (memory / 1048576.0f).ToString("F") + " MB";
                    _MainWindow.ShowMessageBox(message);
                });


            NextButtonClicked = new DelegateCommand(o =>
                {
                    PictureHandler.Instance.ActualPictureIndex++;
                    _MainWindow.ShowPicture(PictureHandler.Instance.ActualPicture.FileName);
                    NameTextBoxText = PictureHandler.Instance.ActualPicture.Name;
                    RefreshStatusBar();
                    PropertyChanged.Invoke(this, new PropertyChangedEventArgs("RatingSliderValue"));

                    if (PictureHandler.Instance.ActualPictureIndex == PictureHandler.Instance.Pictures.Count - 1)
                        NextButtonIsEnabled = false;
                    PreviousButtonIsEnabled = true;

                    _TagProcessingModel.NewPicture();
                });

            PreviousButtonClicked = new DelegateCommand(o =>
                {
                    PictureHandler.Instance.ActualPictureIndex--;
                    _MainWindow.ShowPicture(PictureHandler.Instance.ActualPicture.FileName);
                    NameTextBoxText = PictureHandler.Instance.ActualPicture.Name;
                    RefreshStatusBar();
                    PropertyChanged.Invoke(this, new PropertyChangedEventArgs("RatingSliderValue"));

                    if (PictureHandler.Instance.ActualPictureIndex == 0)
                        PreviousButtonIsEnabled = false;
                    NextButtonIsEnabled = true;

                    _TagProcessingModel.NewPicture();
                });

            // Radio button commands:
            SceneRadioButtonClicked = new DelegateCommand(o =>
                {
                    _TagProcessingModel.SetSelectedKey(TagDatabase.SceneTags);
                });

            PersonRadioButtonClicked = new DelegateCommand(o =>
                {
                    _TagProcessingModel.SetSelectedKey(TagDatabase.PersonTags);
                });

            OtherRadioButtonClicked = new DelegateCommand(o =>
                {
                    _TagProcessingModel.SetSelectedKey(TagDatabase.OtherTags);
                });
            ToFullSizeMenuItemClicked = new DelegateCommand(o =>
                {
                    var actualPicture = PictureHandler.Instance.ActualPicture;
                    _MainWindow.ResizePicture(actualPicture.Width, actualPicture.Height);
                });

            CloseButtonClicked = new DelegateCommand(o =>
                {
                    // Here comes the code (method call), what to do before closing the window.
                    // Saving the workflow.
                    _TagProcessingModel.SaveState();
                    SerializationProvider.SaveData();
                });

            WindowLoaded = new DelegateCommand(o =>
                {
                    // Here comes the code (method call), what to do when the window is loaded.
                    // Tries to open last workflow.
                    _ImageProcessingModel.Open();
                    _TagProcessingModel.FillTagsListBox();
                    _TagProcessingModel.RefillDBTagsList();
                });
        }

        /// <summary>
        /// Enables or disables context specific UI elements.
        /// </summary>
        /// <param name="isEnabled">Wether to enable or disable UI elements.</param>
        internal void SetUIElementsEnabled(bool isEnabled)
        {
            GeneralDataGroupBoxEnabled = isEnabled;
            TagsGroupBoxEnabled = isEnabled;
        }

        /// <summary>
        /// Specifies what happens, when the 'New' menu-item was clicked.
        /// </summary>
        private void OnNewMenuItemClicked()
        {
            TagDatabase.Instance.Clear();
            LocationDatabase.Instance.Clear();
            PictureHandler.Instance.Clear();

            SetUIElementsEnabled(true);

            // Here comes the code (method call), what to do when new image-scan is required.
            // Resetting runtime image-objects.
            PictureHandler.Instance.Pictures.Clear();
            PictureHandler.Instance.ActualPictureIndex = -1;

            string path = _MainWindow.OpenFolder();
            if (string.IsNullOrEmpty(path))
                return;
            _ImageProcessingModel.ScanFolder(path);
        }

        /// <summary>
        /// Actualizes the Status Bar.
        /// </summary>
        internal void RefreshStatusBar()
        {
            PictureHandler pictureHandler = PictureHandler.Instance;
            StatusBarCounter = (pictureHandler.ActualPictureIndex + 1) + "/" + pictureHandler.Pictures.Count;
            if (pictureHandler.ActualPictureIndex != -1 && pictureHandler.Pictures.Count > 0)
            {
                // If there are pictures in the list.
                Picture actualPicture = pictureHandler.Pictures[pictureHandler.ActualPictureIndex];
                StatusBarResolution = "Resolution: " + actualPicture.Width + "x" + actualPicture.Height + " (" + actualPicture.HorizontalResolution + "x" + actualPicture.VerticalResolution + " DPI)";
                StatusBarSize = "Size: " + (actualPicture.FileSize > 1048576 ? (actualPicture.FileSize / 1048576.0f).ToString("F") + " MB" : (actualPicture.FileSize / 1024.0f).ToString("F") + " KB");
            }
        }

        public void TagTextBox_PreviewKeyDown(Key key)
        {
            _TagProcessingModel.TagTextBox_PreviewKeyDown(key);
        }

        /// <summary>
        /// Clears the tag-textbox's content without invoking its method call in TagProcessingModel.
        /// </summary>
        internal void ClearTagTextBox()
        {
            _TagTextBoxText = "";
            PropertyChanged.Invoke(this, new PropertyChangedEventArgs("TagTextBoxText"));
        }

        /// <summary>
        /// Deletes the specified tag from the image.
        /// </summary>
        public void RemoveTagFromImage(string tag)
        {
            _TagProcessingModel.RemoveTagFromImage(tag);
        }

        /// <summary>
        /// Deletes the specified tag from the image.
        /// </summary>
        public void RemoveTagFromDatabase(string tag)
        {
            try
            {
                _TagProcessingModel.RemoveTagFromDatabase(tag);
            }
            catch (Exception ex)
            {
                _MainWindow.ShowMessageBox(ex.Message);
            }
        }
    }
}
